package cn.kivensoft.util;

import java.util.AbstractCollection;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Consumer;

/** 快速Map类型，适用于字典项较少，只读的map
 * @author kiven lee
 * @version 1.0
 * @since 2020-03-02
 */
public class TinyMap implements Map<String, Object> {
	private final Object[] data;

	/** 创建键值对map
	 * @param args 键值对(key1, value1, key2, value2, ...)形式
	 * @return 新创建的SqlArgs对象
	 */
	public static TinyMap of(Object... args) {
		checkArgs(args);
		return new TinyMap(args);
	}

	/** 创建键值对map
	 * @param map 源map, 该map内的所有键值都会被复制
	 * @param args 键值对(key1, value1, key2, value2, ...)形式
	 * @return 新创建的SqlArgs对象
	 */
	public static TinyMap of(Map<String, Object> map, Object... args) {
		checkArgs(args);
		int pos = 0;
		Object[] objs = new Object[(map.size() << 1) + args.length];

		for (Entry<String, Object> entry : map.entrySet()) {
			objs[pos++] = entry.getKey();
			objs[pos++] = entry.getValue();
		}

		if (args.length > 0)
			System.arraycopy(args, 0, objs, pos, args.length);

		return new TinyMap(objs);
	}

	private TinyMap(Object[] data) { this.data = data; }

	private int indexOfKey(Object key) {
		for (int i = 0, n = data.length - 1; i < n; i += 2)
			if (key.equals(data[i]))
				return i;
		return -1;
	}

	@SuppressWarnings("unchecked")
	public final <T> T getx(String key) {
		return (T) get(key);
	}

	public final HashMap<String, Object> toHashMap() {
		HashMap<String, Object> map = new HashMap<>(data.length);
		for (int i = 0, n = data.length - 1; i < n; i += 2)
			map.put((String) data[i], data[i]); 
		return map;
	}

	@Override
	public int size() {
		return data.length >> 1;
	}

	@Override
	public boolean isEmpty() {
		return data.length == 0;
	}

	@Override
	public boolean containsKey(Object key) {
		return indexOfKey(key) != -1;
	}

	@Override
	public boolean containsValue(Object value) {
		for (int i = 1, n = data.length; i < n; i += 2)
			if (value.equals(data[i]))
				return true;
		return false;
	}

	@Override
	public Object get(Object key) {
		int i = indexOfKey(key);
		return (i == -1) ? null : data[i + 1];
	}

	@Deprecated
	@Override
	public Object put(String key, Object value) {
		throw new UnsupportedOperationException();
	}

	@Deprecated
	@Override
	public Object remove(Object key) {
		throw new UnsupportedOperationException();
	}

	@Deprecated
	@Override
	public void putAll(Map<? extends String, ?> m) {
		throw new UnsupportedOperationException();
	}

	@Deprecated
	@Override
	public void clear() {
		throw new UnsupportedOperationException();
	}

    @Override
    public void forEach(BiConsumer<? super String, ? super Object> action) {
    	for (int i = 0, n = data.length - 1; i < n; i += 2)
    		action.accept((String) data[i], data[i + 1]);
    }

	@Override
	public Set<String> keySet() {
		return new KeySet();
	}

	@Override
	public Collection<Object> values() {
		return new Values();
	}

	@Override
	public Set<Entry<String, Object>> entrySet() {
		return new EntrySet();
	}

	private static void checkArgs(Object... args) {
		int len = args.length;
		if ((len & 1) != 0)
			throw new IllegalArgumentException("args count must even numbers");
		for (int i = 0; i < len; i += 2)
			if (args[i].getClass() != String.class)
				throw new IllegalArgumentException(Fmt.fmt("args[{}] must be String", i));
	}

	final static class Node implements Entry<String, Object> {
		String key;
		Object value;
		public String getKey() { return key; }
		public Object getValue() { return value; }
		public Object setValue(Object value) { throw new UnsupportedOperationException(); }
        public int hashCode() {
            return Objects.hashCode(key) ^ Objects.hashCode(value);
        }
        public boolean equals(Object o) {
            if (o == this)
                return true;
            if (o instanceof Map.Entry) {
                Entry<?, ?> e = (Entry<?, ?>) o;
				return Objects.equals(key, e.getKey()) && Objects.equals(value, e.getValue());
            }
            return false;
        }
	}

    abstract class BaseIter {
		int pos;
		public final boolean hasNext() { return pos + 1 < data.length; }
    }

    final class EntryIter extends BaseIter implements Iterator<Entry<String, Object>> {
		Node node = new Node();

		public Entry<String, Object> next() {
			node.key = (String) data[pos];
			node.value = data[pos + 1];
			pos += 2;
			return node;
		}
    }

	final class EntrySet extends AbstractSet<Entry<String, Object>> {
		public int size() { return TinyMap.this.size(); }
		public void clear() { TinyMap.this.clear(); }
		public boolean contains(Object o) { return TinyMap.this.containsKey(o); }
		@Deprecated
		public boolean remove(Object o) { throw new UnsupportedOperationException(); }

		public Iterator<Entry<String, Object>> iterator() {
			return new EntryIter();
		}

		public void forEach(Consumer<? super Entry<String, Object>> action) {
			Node node = new Node();
			for (int i = 0, n = data.length - 1; i < n; i += 2) {
				node.key = (String) data[i];
				node.value = data[i + 1];
				action.accept(node);
			}
		}
	}

    final class KeyIter extends BaseIter implements Iterator<String> {
		public String next() {
			pos += 2;
			return (String) data[pos - 2];
		}
    }

    final class ValueIter extends BaseIter implements Iterator<Object> {
		public Object next() {
			pos += 2;
			return data[pos - 1];
		}
    }

	final class KeySet extends AbstractSet<String> {
		public int size() { return TinyMap.this.size(); }
		public void clear() { TinyMap.this.clear(); }
		public boolean contains(Object o) { return containsKey(o); }
		@Deprecated
		public boolean remove(Object key) { throw new UnsupportedOperationException(); }
		public Iterator<String> iterator() { return new KeyIter(); }

		public void forEach(Consumer<? super String> action) {
			for (int i = 0, n = data.length - 1; i < n; i += 2)
				action.accept((String) data[i]);
		}
	}

	final class Values extends AbstractCollection<Object> {
		public int size() { return TinyMap.this.size(); }
		public void clear() { TinyMap.this.clear(); }
		public boolean contains(Object o) { return containsValue(o); }
		public Iterator<Object> iterator() { return new ValueIter(); }

		public void forEach(Consumer<? super Object> action) {
			for (int i = 1, n = data.length; i < n; ++i)
				action.accept(data[i]);
		}
	}

}
